#pragma once

#include <sstream>
#include "zakengine/zak.h"
#include "basestate.h"
#include <cassert>

using namespace zak;

template <class T>
class StateMachine 
{
public:
	
	// Class constructor
	StateMachine(T* pOwner):
		  m_pOwner(pOwner),
		  m_pCurrentState(NULL),
		  m_pPreviousState(NULL),
		  m_pGlobalState(NULL)
	{

    }
	// State Setters
	void SetCurrentState(BaseState<T>* pCurrentState){m_pCurrentState = pCurrentState;}
	void SetPreviousState(BaseState<T>* pPreviousState){m_pPreviousState = pPreviousState;}
	void SetGlobalState(BaseState<T>* pGlobalState){m_pGlobalState = pGlobalState;}

	// Getters
	BaseState<T>* CurrentState() const{return m_pCurrentState;}
	BaseState<T>* PreviousState() const{return m_pPreviousState;}
	BaseState<T>* GlobalState() const{return m_pGlobalState;}

	// Check to see if passed State is the one in execution
	bool IsInState(const BaseState<T>& pState)const;

	/*-------------------------------------------------------------------------------------------------------------*/
	void Update()const
	{
		// If a Global State exists, execute it
		if(m_pGlobalState)
		{
			m_pGlobalState->Execute(m_pOwner);
		}

		// Same for Current State
		if(m_pCurrentState)
		{
			m_pCurrentState->Execute(m_pOwner);
		}
	}

	/*-------------------------------------------------------------------------------------------------------------*/
	void ChangeState(BaseState<T>* pNewState)
	{
		assert(pNewState && "<StateMachine::ChangeState: trying to change to a NULL state!");

		// Save Previous State
		m_pPreviousState = m_pCurrentState;

		// Call Exit of Current State
		m_pCurrentState->Exit(m_pOwner);

		// Change to New State
		m_pCurrentState = pNewState;

		// Call it's Enter
		m_pCurrentState->Enter(m_pOwner);
	}

	/*-------------------------------------------------------------------------------------------------------------*/
	void RevertToPreviousState()
	{
		ChangeState(m_pPreviousState);
	}

	/*-------------------------------------------------------------------------------------------------------------*/
	bool HandleMessage(const Telegram& message)const
	{
		// First see if the current state is valid and that it can handle the message
		if (m_pCurrentState && m_pCurrentState->OnMessage(m_pOwner, message))
		{
			return true;
		}

		// If not, and if a global state has been implemented, send the message to the GlobalState
		if (m_pGlobalState && m_pGlobalState->OnMessage(m_pOwner, message))
		{
			return true;
		}

		return false;
	}

private:
	// Pointer to the owner of the State
	T*				m_pOwner;

	// Pointer to the current State
	BaseState<T>*	m_pCurrentState;

	// Pointer to the previous State
	BaseState<T>*	m_pPreviousState;

	// Pointer to a global state called everytime the FSM is updated
	BaseState<T>*	m_pGlobalState;
};